using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Bookvar.ObjectModel
{
    /// <summary>
    /// A resource that goes with the topic. 
    /// For example, a small picture, a sound, etc.
    /// </summary>
    [DataContract]
    [Serializable]
    public class Resource : INotifyPropertyChanged, IFileResource
    {
        [DataMember( Name = "ID" )] 
        private readonly Guid id;
        private string resourcePath;
        private ResourceType resourceType;
        private VisualSize visualSize;
        [DataMember] 
        private byte[] resourceBytes;
        [NonSerialized] 
        private object resourceStream;

        #region Properties

        [SuppressMessage( "Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID" )]
        public Guid ID
        {
            get { return id; }
        }

        public FileResourceType FileResourceType
        {
            get { return FileResourceType.Resource; }
        }

        [SuppressMessage( "Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes" )]
        string IFileResource.FileName
        {
            get { return resourcePath; }
        }

        [DataMember]
        public string ResourcePath
        {
            get { return resourcePath; }
            set { resourcePath = value; }
        }

        [DataMember]
        public VisualSize VisualSize
        {
            get { return visualSize; }
            set
            {
                if ( value != visualSize )
                {
                    visualSize = value;
                    OnPropertyChanged( new PropertyChangedEventArgs( "VisualSize" ) );
                }
            }
        }

        [DataMember]
        public ResourceType ResourceType
        {
            get { return resourceType; }
            set { resourceType = value; }
        }

        [SuppressMessage( "Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays" )]
        public byte[] ResourceBytes
        {
            get { return resourceBytes; }
            set { resourceBytes = value; }
        }

        public object ItemSource
        {
            get
            {
                if ( resourceStream == null && resourceBytes != null )
                {
                    resourceStream = DeserialiseResource( resourceBytes );
                }
                return resourceStream;
            }
            set
            {
                if ( value != null )
                {
                    if ( resourceType == ResourceType.Image )
                    {
                        var imageSource = value as ImageSource;
                        if ( imageSource != null )
                        {
                            if ( visualSize != null && visualSize.IsEmpty )
                            {
                                visualSize.Width = imageSource.Width;
                                visualSize.Height = imageSource.Height;
                            }
                            resourceBytes = SerialiseResource( imageSource );
                        }
                    }

                    resourceStream = value;
                    OnPropertyChanged( new PropertyChangedEventArgs( "ItemSource" ) );
                }
            }
        }

        #endregion

        public Resource( Guid id, string resourcePath, ResourceType type )
        {
            this.id = id;
            this.resourcePath = resourcePath;
            resourceType = type;
            visualSize = new VisualSize();
            if ( resourceType == ResourceType.Video )
            {
                visualSize.Width = 320;
                visualSize.Height = 240;
            }
        }

        public Resource( Guid id, string resourcePath )
            : this( id, resourcePath, ResourceType.Image )
        {
        }


        public Resource( string resourcePath, ResourceType type ) : this( Guid.NewGuid(), resourcePath, type )
        {
        }

        public Resource( string resourcePath ) : this( Guid.NewGuid(), resourcePath )
        {
        }

        public Resource() : this( string.Empty )
        {
        }

        private static byte[] SerialiseResource( ImageSource imageSource )
        {
            byte[] result = null;
            BitmapFrame source = imageSource as BitmapFrame;
            if ( source != null )
            {
                PngBitmapEncoder png = new PngBitmapEncoder();
                png.Frames.Add( source );
                using ( MemoryStream mem = new MemoryStream() )
                {
                    png.Save( mem );
                    result = mem.ToArray();
                }
            }
            return result;
        }

        private static object DeserialiseResource( byte[] resourceBytes )
        {
            //HACK: 
            BitmapFrame image;
            using ( MemoryStream stream = new MemoryStream( resourceBytes ) )
            {
                BitmapDecoder decoder;
                try
                {
                    decoder = new PngBitmapDecoder( stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad );
                    
                }
                catch(FileFormatException)
                {
                    stream.Seek( 0, SeekOrigin.Begin );
                    decoder = new JpegBitmapDecoder( stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad );
                }
                image = decoder.Frames[ 0 ];
            }
            return image;
        }

        #region INotifyPropertyChanged Members

        [field : NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged( PropertyChangedEventArgs args )
        {
            if ( PropertyChanged != null )
            {
                PropertyChanged( this, args );
            }
        }

        #endregion
    }
}